package org.example.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.example.bean.MethodInvokeTime;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Description: 监控过滤器
 * Author: e
 * CreateDate: 2021-05-20
 */
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {


    private ConcurrentHashMap<String, List<MethodInvokeTime>> methodCostMap = new ConcurrentHashMap<>();


    {
        ScheduledExecutorService tPMonitorFilterthreadPool = Executors.newSingleThreadScheduledExecutor();
        //每隔5s打印耗时情况
        tPMonitorFilterthreadPool.scheduleAtFixedRate(() -> {
            //检查大于一分钟的请求进行删除
            for (String methodName : methodCostMap.keySet()) {
                List<MethodInvokeTime> invokeTimes = methodCostMap.get(methodName);
                //超过1分钟的，进行移除
                List<MethodInvokeTime> overtimeList = new ArrayList<>();
                for (MethodInvokeTime invokeTime : invokeTimes) {
                    //如果大于1分钟，则移除
                    if ((System.currentTimeMillis() - invokeTime.getStartTime()) > 60000) {
                        overtimeList.add(invokeTime);
                    }
                }
                //删除
                if (overtimeList.size() > 0) {
                    invokeTimes.removeAll(overtimeList);
                    methodCostMap.put(methodName, invokeTimes);
                    System.out.println(methodName + " 当前大于1分钟，移除了" + overtimeList.size() + "个");
                }
            }

            //统计最近1分钟内每个方法的TP90、TP99的耗时情况
            for (String key : methodCostMap.keySet()) {
                //取出每个请求每次请求时间list
                List<MethodInvokeTime> methodInvokeTimeList = methodCostMap.get(key);
                //把请求时间存储导list中
                List<Long> costs = methodInvokeTimeList.stream().map(MethodInvokeTime::getCost).sorted().collect(Collectors.toList());
                //排序
                //请求总数
                int requestCount = costs.size();
                //计算TP90
                long tp90 = costs.get((int) (requestCount * 0.9));
                //计算TP99
                long tp99 = costs.get((int) (requestCount * 0.99));
                System.out.println(key + ",请求总数:" + requestCount + " TP90: " + tp90 + " ms --- TP99: " + tp99 + " ms");
            }
            System.out.println("---------------------------------------------------------------------------------");
        }, 0, 5, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //异步调用不进行记录
        if (!RpcUtils.isAsync(invoker.getUrl(), invocation)) {
            long startTime = System.currentTimeMillis();
            //sayHello1 或 sayHello2 或 sayHello3
            String methodName = invocation.getMethodName();
            //org.example.service.HelloService
            String interfaceName = invoker.getInterface().getName();
            // key = org.example.service.HelloService.sayHello1
            String key = interfaceName + "." + methodName;
            try {
                //执行
                return invoker.invoke(invocation);
            } finally {
                //记录每个方法请求耗时
                MethodInvokeTime methodInvokeTime = new MethodInvokeTime(startTime
                        , System.currentTimeMillis(), key);
                //如果当前方法在map 中，则直接添加
                if (methodCostMap.containsKey(key)) {
                    methodCostMap.get(key).add(methodInvokeTime);
                } else {
                    List<MethodInvokeTime> methodInvokeTimeList = new ArrayList<>();
                    methodInvokeTimeList.add(methodInvokeTime);
                    methodCostMap.put(key, methodInvokeTimeList);
                }
            }
        }
        return invoker.invoke(invocation);
    }
}
